using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Microblaze.Elf
{
  public class ElfFile
  {
    #region Fields
    private BinaryReader reader;    
    private ElfIdentification identification;
    private ElfHeader header;
    private List<ElfSectionHeader> sectionHeaders;
    private List<ElfProgramHeader> programHeaders;
    private ElfStringTable stringTable;

    private ElfStringTable symbolStringTable;

    private ElfSymbolTable symbolTable;

    public ElfSymbolTable SymbolTable
    {
      get { return symbolTable; }
      set { symbolTable = value; }
    }
    #endregion

    #region Properties
    public BinaryReader Reader
    {
      get { return reader; }
    }

    public ElfIdentification Identification
    {
      get { return identification; }
    }
    
    public ElfHeader Header
    {
      get { return header; }      
    }
    
    public List<ElfSectionHeader> SectionHeaders
    {
      get { return sectionHeaders; }      
    }

    public List<ElfProgramHeader> ProgramHeaders
    {
      get { return programHeaders; }
    }
    #endregion

    public ElfFile(BinaryReader reader)
    {
      this.reader = reader;
      this.sectionHeaders = new List<ElfSectionHeader>();
      this.programHeaders = new List<ElfProgramHeader>();
    }

    public string GetNameFromMainStringTable(uint index)
    {
      if (stringTable == null)
        return "table not set";
      else
        return stringTable.GetString(index);
    }

    public String GetNameFromSymbolStringTable(uint index)
    {
      if (stringTable == null)
        return "table not set";
      else
        return symbolStringTable.GetString(index);
    }

    public static ElfFile OpenAndProcessFile(string filename)
    {
      FileStream stream = null;
      BinaryReader reader = null;
      ElfFile elfFile = null;

      try
      {
        stream = File.Open(filename, FileMode.Open);
        reader = new BinaryReader(stream);

        elfFile = processFile(reader);
      }
      finally
      {
        if (reader != null)
          reader.Close();

        if (stream != null)
          stream.Close();
      }

      return elfFile;
    }

    private static ElfFile processFile(BinaryReader reader)
    {
      ElfFile file = new ElfFile(reader);
      file.Read();

      /*
      Console.WriteLine(file.Header);

      foreach (ElfSectionHeader s in file.SectionHeaders)
        Console.WriteLine(s);

      foreach (ElfProgramHeader p in file.ProgramHeaders)
        Console.WriteLine(p);
      */

      return file;
    }

    public void Read()
    { 
      // read the "elf identification" first. Tells us
      // how to interpreted this elf file
      this.identification = new ElfIdentification();
      this.identification.Read(reader);
  
      // read the header       
      this.header = new ElfHeader(this);
      this.header.Read();

      // set the file position to the start of the section header table
      this.reader.BaseStream.Seek((long)this.header.E_shoff, 0);

      for (int i = 0; i < header.E_shnum; i++)
      {
        ElfSectionHeader sectionHeader = new ElfSectionHeader(this);
        sectionHeader.Read();

        // add to list ..
        this.sectionHeaders.Add(sectionHeader);

        // is this a symbol table?
        if (sectionHeader.Sh_type == ElfSectionHeader.Sh_Type.SHT_SYMTAB)
        {
          // set it as THE symbol table and hope there is only one ;)
          symbolTable = new ElfSymbolTable(sectionHeader);
          symbolTable.Read();

          // associate the section with this new symbol table
          sectionHeader.AssociatedEntity = symbolTable;          
        }
        else if (sectionHeader.Sh_type == ElfSectionHeader.Sh_Type.SHT_STRTAB)
        {
          ElfStringTable stringTable = new ElfStringTable(sectionHeader);
          stringTable.Read();

          sectionHeader.AssociatedEntity = stringTable;
        }
      }

      // locate the (main) string table
      this.stringTable = 
        sectionHeaders[header.E_shstrndx].AssociatedEntity as ElfStringTable;    
  
      // locate the symbol string table 
      foreach (ElfSectionHeader sectionHeader in sectionHeaders)
      {
        if (sectionHeader.Name == ".strtab")
        {
          this.symbolStringTable =
            sectionHeader.AssociatedEntity as ElfStringTable;
        }
      }

      // set the file position to the start of the section header table
      this.reader.BaseStream.Seek((long)this.header.E_phoff, 0);

      for (int i = 0; i < header.E_phnum; i++)
      {
        ElfProgramHeader programHeader = new ElfProgramHeader(this);
        programHeader.Read();

        // add to list ..
        this.programHeaders.Add(programHeader);
      }

      // symbolTable.DumpToConsole();
    }
  }
}
